/*
 * LinkedList.h
 *
 *  Created on: 21.12.2012
 *      Author: Michael
 */

#ifndef LINKEDLIST_H_
#define LINKEDLIST_H_
#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
	int _value;
	struct Node* _next;
	struct Node* _prev;
} Node_t;

typedef struct LinkedList {
	Node_t* _first;
	int _size;
	Node_t* (*get)(struct LinkedList* pThis, int index);
	int (*set)(struct LinkedList* pThis, Node_t* pNode, int index);
	int (*add)(struct LinkedList* pThis, Node_t* pNode);
	void (*print)(struct LinkedList* pThis);
} LinkedList_t;

/* get the node of the specified index */
Node_t* get(LinkedList_t* pThis, int index);

/*
 * set a node at a specified index
 * if the index already exists shift the existing one to the right
 * if the index is to high, the node will be added at the end of the list
 * return: The index on which the node was added
 */
int set(LinkedList_t* pThis, Node_t* pNode, int index);

/*
 * Create a new LinkedList
 */
LinkedList_t* createList();

/*
 * add a node at the end
 * return: The index on which the node was added
 */
int add(LinkedList_t* pThis, Node_t* pNode);

/* print the list content */
void print(LinkedList_t* pThis);

LinkedList_t* createList() {
	LinkedList_t* pCreate;
	pCreate = malloc(sizeof(LinkedList_t*));
	pCreate->_first = NULL;
	pCreate->_size = 1;
	pCreate->add = add;
	pCreate->set = set;
	pCreate->get = get;
	pCreate->print = print;

	return pCreate;
}

Node_t* get(LinkedList_t* pThis, int index) {
	Node_t* pCurr = pThis->_first;
	int i;
	for (i = 0; i < index; ++i) {
		pCurr = pCurr->_next;
	}

	return pCurr;
}

int set(LinkedList_t* pThis, Node_t* pNode, int index) {
	unsigned int i;
	Node_t* pCurr;

	/* index to high, add the node at the end */
	if (index >= pThis->_size || pThis->_first == NULL ) {
		return pThis->add(pThis, pNode);
	}

	/* add at the beginning */
	if (index == 0) {
		Node_t* pTmp = pThis->_first;
		pThis->_first = pNode;
		pNode->_next = pTmp;
		pNode->_prev = NULL;
		pThis->_size++;
		return 0;
	}

	/* find the correct place for the node */
	pCurr = pThis->_first;
	for (i = 1; i < index; i++) {
		pCurr = pCurr->_next;
	}

	if (pCurr->_next == NULL ) {
		pCurr->_next = pNode;
		pNode->_prev = pCurr;
	} else {
		Node_t* pTmpNext = pCurr->_next;
		pCurr->_next = pNode;
		pNode->_next = pTmpNext;
		pNode->_prev = pCurr;
		pTmpNext->_prev = pNode;
	}

	pThis->_size++;
	return index;
}

int add(LinkedList_t* pThis, Node_t* pNode) {
	unsigned int i;
	Node_t* pCurr;
	int index = pThis->_size;

	/* check the first node */
	if (pThis->_first == NULL ) {
		pThis->_first = pNode;
		pNode = NULL;
		return 0;
	}

	/* get the last node */
	pCurr = pThis->_first;
	for (i = 1; i < pThis->_size; i++) {
		pCurr = pCurr->_next;
	}

	/* add the new node */
	pCurr->_next = pNode;
	 pNode->_prev = pCurr;
	pThis->_size++;

	return index;
}

void print(LinkedList_t* pThis) {
	unsigned int i;
	Node_t* pCurr = pThis->_first;
	for (i = 0; i < pThis->_size; i++) {
		printf("[%d] %d\n", i, pCurr->_value);
		pCurr = pCurr->_next;
	}
	printf("\n");
}

#endif /* LINKEDLIST_H_ */
